package com.security.demo;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.ECKeyUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

/**
 * @Description: SM2加密的简单DEMO
 */
public class SM2Demo {

    // 填写你自己生成的私钥，形如：
    // 2d06e738e864ba5e384da84eb3156516600b87644a9019600944203d5ad7be23
    public static String PRIVATE_KEY = "86A6905597E7C26CCE1F1DE30B9607FBF395A41C7CEAD56C345C24B75540B1F5";
    // 填写你自己生成的公钥，形如：
    // 0457beded4312c0293cb78ec15b0f50ea47c49802e9dcd4547383fd3585b82ca2088bb5fa0796c28542710e20e78291376b0748f08e5eb76c2f6f5119d8ecca225
    public static String PUBLIC_KEY = "04A6224256F46F5413AE9734BE5959C1144E11B206C3A20F20A0FF24635A1A690202020B76D4A1C770E47F5E83F2C48489EBA2ACD5E69F6FD85479EC27538C04FB";


    public static void main(String[] args) {
        getSM2KeyPair();
    }

    /**
     * 获取一对随机的SM2私钥和公钥，注意公钥是以04标志位开头的
     * 这里获取并打印后，将获取到的密钥填入上面的常量里，供后续使用
     */
    public static void getSM2KeyPair(){
        SM2 sm2 = SmUtil.sm2();
        byte[] privateKey = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
        // 这里选择不压缩，携带标志位04
        byte[] publicKey = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
        //打印当前的公私秘钥，复制下来供之后使用
        System.out.println("私钥: " + HexUtil.encodeHexStr(privateKey));
        System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey));
    }

    /**
     * SM2解密
     * @param param 密文
     * @return 解密后的明文字符串
     */
    public static String decrypt(String param) throws Exception {
        SM2 sm2 = SmUtil.sm2(ECKeyUtil.toSm2PrivateParams(PRIVATE_KEY), ECKeyUtil.toSm2PublicParams(PUBLIC_KEY));
        String paramBeforeDecrypt;
        // 使用BC库进行解密时需要在密文前面加标志位04
        if (!param.startsWith("04")) {
            paramBeforeDecrypt = "04" + param;
        } else {
            paramBeforeDecrypt = param;
        }
        byte[] decryptFromBcd = sm2.decryptFromBcd(paramBeforeDecrypt, KeyType.PrivateKey);
        if (decryptFromBcd != null && decryptFromBcd.length > 0){
            return StrUtil.utf8Str(decryptFromBcd);
        }else {
            throw new Exception("解密失败");
        }
    }

    /**
     * SM2加密
     * @param param 加密前的明文
     * @return 加密后的密文
     */
    public static String encrypt(String param) {
        SM2 sm2 = SmUtil.sm2(ECKeyUtil.toSm2PrivateParams(PRIVATE_KEY), ECKeyUtil.toSm2PublicParams(PUBLIC_KEY));
        String encryptBcd = sm2.encryptBcd(param, KeyType.PublicKey);
        if (encryptBcd != null ){
            // 跟前端(使用sm-crypto库)调试时，前端解密时不能带标志位04，这里做处理去掉密文最前面的04标志位
            if (encryptBcd.startsWith("04")){
                encryptBcd = encryptBcd.substring(2);
            }
            // 前端解密时只能解小写形式的16进制数据（A-F不行，a-f可），这里将所有大写字母转化为小写
            encryptBcd = encryptBcd.toLowerCase();
        }
        return encryptBcd;
    }
}
